/*
 * shell.c
 *
 *  Created on: 2011-07-13
 *      Author: byron
 *
 *      based on u-boot shell
 *
 *  __u_boot_cmd_start = .;
 *	.u_boot_cmd : { *(.u_boot_cmd) }
 *	__u_boot_cmd_end = .;
 *
 */

//#include "drivers/serial/serial.h"
#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>
#include <semphr.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
//#include <rtc.h>
//#include <ff.h>
//#include <sdcard.h>
//#include <debug.h>
#include <time.h>
#include <command.h>
#include "config.h"
#include "serial.h"

#include "GUI.h"

//struct rtc_t rtc;
struct tm epoch_strt;
time_t basetime,basetime1;
time_t curtime;
double nsecs;
int lprintf(const char *format, ...);

#define	CFG_MAXARGS		16		/* max number of command args	*/
#define CFG_CBSIZE 64
#define	CFG_PROMPT		"$ " /* Monitor Command Prompt	*/
#define CMD_FLAG_REPEAT		0x0001	/* repeat last command		*/

char        console_buffer[CFG_CBSIZE];		/* console I/O buffer	*/
static char   tab_seq[] = "        ";		/* used to expand TABs	*/
static char erase_seq[] = "\b \b";		/* erase sequence	*/

static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen);
//int vSerialPutString( int pxPort, const unsigned char * const pcString, unsigned short usStringLength );

#define puts_(s) xSerialPutString( DEBUG_UART, s, strlen(s) );
static int znaka=0;

char get_console()
{
	unsigned char s,d;

	d = xSerialGetChar( DEBUG_UART, &s, 500);
	if(d==0)
	{
		znaka=0;
	}
	else
	{
		znaka=1;
		GUI_StoreKeyMsg(s, 1);
	}
	return s;
}

char write_console(char d)
{
	xSerialPutChar( DEBUG_UART, d, 500);
	return d;
}
/*
 * Prompt for input and read a line.
 * If  CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
 * time out when time goes past endtime (timebase time in ticks).
 * Return:	number of read characters
 *		-1 if break
 *		-2 if timed out
 */
int readline (const char *const prompt)
{
#ifdef CONFIG_CMDLINE_EDITING
	char *p = console_buffer;
	unsigned int len=MAX_CMDBUF_SIZE;
	int rc;
	static int initted = 0;

	if (!initted) {
		hist_init();
		initted = 1;
	}

	puts_ (prompt);

	rc = cread_line(p, &len);
	return rc < 0 ? rc : len;
#else
	char   *p = console_buffer;
	int	n = 0;				/* buffer index		*/
	int	plen = 0;			/* prompt length	*/
	int	col;				/* output column cnt	*/
	char	c;

	/* print prompt */
	if (prompt) {
		plen = strlen (prompt);
		puts_ (prompt);
	}
	col = plen;

	for (;;) {
		c = get_console();
		if(znaka==1)
		{
			/*
			 * Special character handling
			 */
			switch (c) {
			case '\r':				/* Enter		*/
			case '\n':
				*p = '\0';
				puts_ ("\r\n");
				return (p - console_buffer);

			case '\0':				/* nul			*/
				continue;

			case 0x03:				/* ^C - break		*/
				console_buffer[0] = '\0';	/* discard input */
				return (-1);

			case 0x15:				/* ^U - erase line	*/
				while (col > plen) {
					puts_ (erase_seq);
					--col;
				}
				p = console_buffer;
				n = 0;
				continue;

			case 0x17:				/* ^W - erase word 	*/
				p=delete_char(console_buffer, p, &col, &n, plen);
				while ((n > 0) && (*p != ' ')) {
					p=delete_char(console_buffer, p, &col, &n, plen);
				}
				continue;

			case 0x08:				/* ^H  - backspace	*/
			case 0x7F:				/* DEL - backspace	*/
				p=delete_char(console_buffer, p, &col, &n, plen);
				continue;

			default:
				/*
				 * Must be a normal character then
				 */
				if (n < CFG_CBSIZE-2) {
					if (c == '\t') {	/* expand TABs		*/
#ifdef CONFIG_AUTO_COMPLETE
						/* if auto completion triggered just continue */
						*p = '\0';
						if (cmd_auto_complete(prompt, console_buffer, &n, &col)) {
							p = console_buffer + n;	/* reset */
							continue;
						}
#endif
						puts_ (tab_seq+(col&07));
						col += 8 - (col&07);
					} else {
						++col;		/* echo input		*/
						write_console(c);
						//					printf("%s", c);
					}
					*p++ = c;
					++n;
				} else {			/* Buffer full		*/
					write_console ('\a');
				}
			}
		}
	}
#endif /* CONFIG_CMDLINE_EDITING */
}

static char * delete_char (char *buffer, char *p, int *colp, int *np, int plen)
{
	char *s;

	if (*np == 0) {
		return (p);
	}

	if (*(--p) == '\t') {			/* will retype the whole line	*/
		while (*colp > plen) {
			puts_ (erase_seq);
			(*colp)--;
		}
		for (s=buffer; s<p; ++s) {
			if (*s == '\t') {
				puts_ (tab_seq+((*colp) & 07));
				*colp += 8 - ((*colp) & 07);
			} else {
				++(*colp);
				write_console(*s);
				//printf("%s", *s);
			}
		}
	} else {
		puts_ (erase_seq);
		(*colp)--;
	}
	(*np)--;
	return (p);
}



extern xTaskHandle xHandle;
int kill(int argc, char *argv[])
{
	int l;
	(void) argc;
	l = atoi(argv[1]);
//	lprintf("THREAD NR=%d\n",l);
	//	vTaskKill( xHandle );
	return 0;
}


int free__(int argc, char *argv[])
{
	uint32_t l;
	(void) argc;
	(void) argv;
	l = xPortGetFreeHeapSize();
//	lprintf("MEM FREE=%d\n",l);
	return 0;
}


//extern p_xm_send_func xmodem_out_func;
//extern p_xm_recv_func xmodem_in_func;

void out_rs(unsigned char d)
{
	xSerialPutChar( 2, d, 500);
}

int in_rs(int delay)
{
	int d;
	unsigned char s;
	//	unsigned int xDelay = delay / portTICK_RATE_MS;

	d = xSerialGetChar( 2, &s, delay);
	if(d==0)
		return -1;
	else return s;
}

char *data;
int rxd(int argc, char *argv[])
{
	(void) argc;
	(void) argv;
	//	xmodem_out_func = out_rs;
	//	xmodem_in_func =  in_rs;
	//	data = pvPortMalloc(1024*90);
	//	lprintf("read %d\n", xmodem_receive(&data));
	return 0;
}

int (*test_shell)(int argc, char *argv[]);


/****************************************************************************
 * returns:
 *	1  - command executed, repeatable
 *	0  - command executed but not repeatable, interrupted commands are
 *	     always considered not repeatable
 *	-1 - not executed (unrecognized, bootd recursion or too many args)
 *           (If cmd is NULL or "" or longer than CFG_CBSIZE-1 it is
 *           considered unrecognized)
 *
 * WARNING:
 *
 * We must create a temporary copy of the command since the command we get
 * may be the result from getenv(), which returns a pointer directly to
 * the environment data, which may change magicly when the command we run
 * creates or modifies environment variables (like "bootp" does).
 */
static void process_macros (const char *input, char *output)
{
	char c, prev;
	const char *varname_start = NULL;
	int inputcnt = strlen (input);
	int outputcnt = CFG_CBSIZE;
	int state = 0;		/* 0 = waiting for '$'  */

	/* 1 = waiting for '(' or '{' */
	/* 2 = waiting for ')' or '}' */
	/* 3 = waiting for '''  */
#ifdef DEBUG_PARSER
	char *output_start = output;

	printf ("[PROCESS_MACROS] INPUT len %d: \"%s\"\n", strlen (input),
			input);
#endif

	prev = '\0';		/* previous character   */

	while (inputcnt && outputcnt) {
		c = *input++;
		inputcnt--;

		if (state != 3) {
			/* remove one level of escape characters */
			if ((c == '\\') && (prev != '\\')) {
				if (inputcnt-- == 0)
					break;
				prev = c;
				c = *input++;
			}
		}

		switch (state) {
		case 0:	/* Waiting for (unescaped) $    */
			if ((c == '\'') && (prev != '\\')) {
				state = 3;
				break;
			}
			if ((c == '$') && (prev != '\\')) {
				state++;
			} else {
				*(output++) = c;
				outputcnt--;
			}
			break;
		case 1:	/* Waiting for (        */
			if (c == '(' || c == '{') {
				state++;
				varname_start = input;
			} else {
				state = 0;
				*(output++) = '$';
				outputcnt--;

				if (outputcnt) {
					*(output++) = c;
					outputcnt--;
				}
			}
			break;
		case 2:	/* Waiting for )        */
			if (c == ')' || c == '}') {
				int i;
				char envname[CFG_CBSIZE], *envval;
				int envcnt = input - varname_start - 1;	/* Varname # of chars */

				/* Get the varname */
				for (i = 0; i < envcnt; i++) {
					envname[i] = varname_start[i];
				}
				envname[i] = 0;

				/* Get its value */
				envval = (char*)getenv (envname);

				/* Copy into the line if it exists */
				if (envval != NULL)
					while ((*envval) && outputcnt) {
						*(output++) = *(envval++);
						outputcnt--;
					}
				/* Look for another '$' */
				state = 0;
			}
			break;
		case 3:	/* Waiting for '        */
			if ((c == '\'') && (prev != '\\')) {
				state = 0;
			} else {
				*(output++) = c;
				outputcnt--;
			}
			break;
		}
		prev = c;
	}

	if (outputcnt)
		*output = 0;

#ifdef DEBUG_PARSER
	printf ("[PROCESS_MACROS] OUTPUT len %d: \"%s\"\n",
			strlen (output_start), output_start);
#endif
}

int parse_line (char *line, char *argv[])
{
	int nargs = 0;

#ifdef DEBUG_PARSER
	printf ("parse_line: \"%s\"\n", line);
#endif
	while (nargs < CFG_MAXARGS) {

		/* skip any white space */
		while ((*line == ' ') || (*line == '\t')) {
			++line;
		}

		if (*line == '\0') {	/* end of line, no more args	*/
			argv[nargs] = NULL;
#ifdef DEBUG_PARSER
			printf ("parse_line: nargs=%d\n", nargs);
#endif
			return (nargs);
		}

		argv[nargs++] = line;	/* begin of argument string	*/

		/* find end of string */
		while (*line && (*line != ' ') && (*line != '\t')) {
			++line;
		}

		if (*line == '\0') {	/* end of line, no more args	*/
			argv[nargs] = NULL;
#ifdef DEBUG_PARSER
			printf ("parse_line: nargs=%d\n", nargs);
#endif
			return (nargs);
		}

		*line++ = '\0';		/* terminate current arg	 */
	}

	printf ("** Too many args (max. %d) **\n", CFG_MAXARGS);

#ifdef DEBUG_PARSER
	printf ("parse_line: nargs=%d\n", nargs);
#endif
	return (nargs);
}

void hvm1_thread(void *args);

void vTASK(void *args);

void (*main_entry)(void) = (void*)0xa1800000;


int do_nand(int argc, char *argv[]);
extern int io_irq_cnt;

void
sockex_nonblocking_connect(int argc, char **argv);

int run_command (const char *cmd, int flag)
{
		cmd_tbl_t *cmdtp;
	char cmdbuf[CFG_CBSIZE];	/* working copy of cmd		*/
	char *token;			/* start of token in cmdbuf	*/
	char *sep;			/* end of token (separator) in cmdbuf */
	char finaltoken[CFG_CBSIZE];
	char *str = cmdbuf;
	char *argv[CFG_MAXARGS + 1];	/* NULL terminated	*/
	int argc, inquotes;
	int repeatable = 1;
	int rc = 0;

	//	nand_info_t *nand;


#ifdef DEBUG_PARSER
	printf ("[RUN_COMMAND] cmd[%p]=\"", cmd);
	puts (cmd ? cmd : "NULL");	/* use puts - string may be loooong */
	puts ("\"\n");
#endif

	//	clear_ctrlc();		/* forget any previous Control C */

	if (!cmd || !*cmd) {
		return -1;	/* empty command */
	}

	if (strlen(cmd) >= CFG_CBSIZE) {
		puts ("## Command too long!\n");
		return -1;
	}

	strcpy (cmdbuf, cmd);
	/* Process separators and check for invalid
	 * repeatable commands
	 */

#ifdef DEBUG_PARSER
	printf ("[PROCESS_SEPARATORS] %s\n", cmd);
#endif
	while (*str) {
		/*
		 * Find separator, or string end
		 * Allow simple escape of ';' by writing "\;"
		 */
		for (inquotes = 0, sep = str; *sep; sep++) {
			if ((*sep=='\'') &&
					(*(sep-1) != '\\'))
				inquotes=!inquotes;

			if (!inquotes &&
					(*sep == ';') &&	/* separator		*/
					( sep != str) &&	/* past string start	*/
					(*(sep-1) != '\\'))	/* and NOT escaped	*/
				break;
		}

		/*
		 * Limit the token to data between separators
		 */
		token = str;
		if (*sep) {
			str = sep + 1;	/* start of command for next pass */
			*sep = '\0';
		}
		else
			str = sep;	/* no more commands for next pass */
#ifdef DEBUG_PARSER
		printf ("token: \"%s\"\n", token);
#endif

		/* find macros in this token and replace them */
		process_macros (token, finaltoken);

		/* Extract arguments */
		if ((argc = parse_line (finaltoken, argv)) == 0) {
			rc = -1;	/* no command at all */
			continue;
		}

		/* Look up command in command table */
		if ((cmdtp = find_cmd(argv[0])) == NULL) {
//			lprintf ("Unknown command '%s' - try 'help'\n", argv[0]);
//			lprintf ("Unknown command '%s' - try 'help'\n", argv[0]);
puts_("Unknown command\n");
			rc = -1;	/* give up after bad command */
			continue;
		}

		/* found - check max args */
		if (argc > cmdtp->maxargs) {
//			lprintf ("Usage:\n%s\n", cmdtp->usage);
			puts_("Usage\n");
			rc = -1;
			continue;
		}

		cmdtp->cmd(cmdtp, flag, argc, argv);
	}
	return rc ? rc : repeatable;
}


void shell_loop(void *arg)
{
	(void)arg;
	static char lastcommand[CFG_CBSIZE] = { 0, };
	int len;
	int rc = 1;
	int flag;

	vSerialInit(DEBUG_UART, 115200);

	vTaskDelay(1000);
	for (;;)
	{
//		if(xSerialGetChar( 0, &d, 100)==1)
//		xSerialPutChar(0, d, 0);

#if 1
		len = readline (CFG_PROMPT);
		//		printf("len = %d\n", len);
		flag = 0;	/* assume no special flags for now */
		if (len > 0)
			strcpy (lastcommand, console_buffer);
		else if (len == 0)
			flag |= CMD_FLAG_REPEAT;

		if (len == -1)
		{
			puts_("<INTERRUPT>\n");
		}
		else
		{
//			lprintf("Command\n");
			puts_("Command\n");
			rc = run_command (lastcommand, flag);
		}
		if (rc <= 0) {
			/* invalid command or not repeatable, forget it */
			lastcommand[0] = 0;
		}
#endif
	}
}
